#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include <ctype.h>

#include "./comm_head.h"

typedef struct
{
	uint8_t mac1;
	uint8_t mac2;
	uint8_t mac3;
	uint8_t mac4;
	uint8_t mac5;
	uint8_t mac6;
} mac_addr_t __attribute__((packed));

typedef struct
{
	mac_addr_t dest_mac;
	mac_addr_t src_mac;
	uint16_t   mac_protocol_type;    //默认0x9F99
	uint8_t    protocol_type:5;      //默认 0
	uint8_t    version:3;            //默认 1
	uint8_t    tunnel;               //默认命令包 0x01
	uint16_t   group;                //分组
	uint16_t   frame_len:13;         //帧长度，TLVs长度+2个字节frame_sq
	uint16_t   priority:3;           //优先级，忽略
	uint16_t   frame_sq:15;          //帧序号，原样返回
	uint16_t   req_resp_type:1;      //0请求 1应答
} req_packet_eth_header_t __attribute__((packed));

u_int8_t packet[4096];
u_int8_t *preq = NULL;


static unsigned int crc_table[256];
static void init_crc_table(void);
static unsigned int crc32(unsigned int crc, unsigned char * buffer, unsigned int size);
static int calc_img_crc(const char *buf, int len, unsigned int *img_crc);


/*
   测试数据：
   输入：
   0x20000000,0x003,0x01,0x02,0x03,0x55,0x00,0xaa,79,9a
   输出：
   0x0 0x0 0x0 0x20 0x3 0x0 0x1 0x2 0x3 0x55 0x0 0xaa 0x79 0x9a
*/
u_int8_t ByteOutData[4096];
u_int8_t ByteInData[4096];
bool ascii2hex(char** q, int* ret_val, long* pcount);
int  valuecnt(long val);
void init_packet_head(u_int16_t len);
void dump_data(uint8_t *data, int len);

int main(int argc, char* argv[])
{
	int ret_err = 0;
	int rw_flag = 0;//1:read 0:write
	init_crc_table();

	char arg[4096];
	/*
	 * 打印输入的字符串参数并拼接成一个完整字符串
	 */
	int i,offset = 0;
	for(i = 1; i < argc; i++)
	{
		strcpy(&arg[offset], argv[i]);
		offset += strlen(argv[i]);
	}
	/*
	 * 将字符串转换为对应的16进制数据
	 */
	char* p = &arg[0];
	int  out_len = 0, tmp = 0;
    long cnt = 0;
	while(*p != 0)
	{
		if(ascii2hex(&p,&tmp,&cnt))
		{
#if 1
			cnt = valuecnt(cnt);
			do	
			{	
				ByteOutData[out_len++] = tmp;
				tmp >>= 8;
			}while(--cnt);
#endif
		}
	}

	if(*argv[1] == 'r' || *argv[1] == 'R')
	{
		ByteOutData[3] |= 0x80;
		rw_flag = 1;
	}
	else
	{
		rw_flag = 0;
	}

	const int packet_payload_off = sizeof(req_packet_eth_header_t);
	/*
	 * 组成数据帧格式发送到串口
	 */
	u_int32_t crc;
	init_packet_head(out_len);
	memcpy(packet+packet_payload_off, ByteOutData, out_len);
	calc_img_crc(packet, packet_payload_off+out_len, &crc);
	crc = ~crc;
	memcpy(packet+packet_payload_off+out_len, &crc, 4);

	int resq_tag = ((packet[packet_payload_off+3]&0X7F) << 24) + (packet[packet_payload_off+2] << 16) + (packet[packet_payload_off+1] << 8) + packet[packet_payload_off];
	int resq_len = (packet[packet_payload_off+5] << 8) + packet[packet_payload_off+4];
	int resq_data_off = packet_payload_off + 6;
	int resq_data_len = out_len;

#if 0 //used for debug
	printf("=============================\r\n");
	printf("resq_tag=%x\r\n",resq_tag);
	printf("resq_len=%x\r\n",resq_len);
	printf("resq_data_off=%x\r\n",resq_data_off);
	printf("resq_data_len=%x\r\n",resq_data_len);

	dump_data(&packet[0], sizeof(req_packet_eth_header_t)+out_len+4);
	printf("\r\n=============================\r\n\r\n");
#endif
	/*
	 * 发送数据到串口并从串口获取响应数据
	 */
	int r_len = 0;	
	open_and_wr_serial(packet,sizeof(req_packet_eth_header_t)+out_len+4,ByteInData, sizeof(ByteInData), &r_len);
	/*
	 * 解析接收的响应数据
	 */
	int resp_tag = ((ByteInData[packet_payload_off+3]&0X7F) << 24) + (ByteInData[packet_payload_off+2] << 16) + (ByteInData[packet_payload_off+1] << 8) + ByteInData[packet_payload_off];
	int resp_len = (ByteInData[packet_payload_off+5] << 8) + ByteInData[packet_payload_off+4];
	int resp_data_off = packet_payload_off + 6;
	int resp_data_len = r_len - resp_data_off - 4;

#if 0 //used for debug
	printf("=============================\r\n");
	printf("resp_tag=%x\r\n",resp_tag);
	printf("resp_len=%x\r\n",resp_len);
	printf("resp_data_off=%x\r\n",resp_data_off);
	printf("resp_data_len=%x\r\n",resp_data_len);

	dump_data(&ByteInData[0], r_len);

	printf("\r\n=============================\r\n\r\n");
#endif
	if (rw_flag == 0)         //write
	{
		printf("write tag=0x%08x len=0x%02x    ",resq_tag, resq_len);
		if ((resp_tag == 0x2F000002) && (resp_len == 2))
		{
			if ((ByteInData[resp_data_off] == 0) && (ByteInData[resp_data_off+1] == 0))
			{
				printf("successed.\r\n");
				ret_err = 0;
			}
			else
			{
				printf("failed. error code=0x%04x \r\n", (ByteInData[resp_data_off+1]<<8)+ByteInData[resp_data_off]);
				ret_err = 1;
			}
		}
		else
		{
			printf("write failed. unknow response packet!\r\n");
			ret_err = 2;
		}
	}
	else	//read
	{
		printf("read  tag=0x%08x len=0x%02x    ",resq_tag, resq_len);
		if (resq_tag == resp_tag)
		{
			ret_err = 0;
			printf("successed.");
			dump_data(&ByteInData[resp_data_off], resp_data_len);
			printf("\r\n");
		}
		else if ((resp_tag == 0x2F000002) && (resp_len == 2))
		{
			printf("failed. error code=0x%04x \r\n", (ByteInData[resp_data_off+1]<<8)+ByteInData[resp_data_off]);
			ret_err = 3;
		}
		else
		{
			printf("failed. unknow response packet!\r\n");
			ret_err = 4;
		}
	}

	return ret_err;
}

void dump_data(uint8_t *data, int len)
{
	for (int i = 0; i < len; i++)
	{
		if ((i%16) == 0)
		{
			printf("\r\n");
		}
		printf("0x%02x ", data[i]);
	}
}

void init_packet_head(u_int16_t len)
{
	req_packet_eth_header_t *phead = (req_packet_eth_header_t*)&packet[0]; 

	phead-> dest_mac.mac1 = 0x11;
	phead-> dest_mac.mac2 = 0x22;
	phead-> dest_mac.mac3 = 0x33;
	phead-> dest_mac.mac4 = 0x44;
	phead-> dest_mac.mac5 = 0x55;
	phead-> dest_mac.mac6 = 0x66;
	
	phead-> src_mac.mac1 = 0xeb;
	phead-> src_mac.mac2 = 0x90;
	phead-> src_mac.mac3 = 0xeb;
	phead-> src_mac.mac4 = 0x90;
	phead-> src_mac.mac5 = 0xeb;
	phead-> src_mac.mac6 = 0x90;
	
	phead-> mac_protocol_type = 0x9F99;
	phead-> version           = 0x1;
	phead-> protocol_type     = 0x00;
	phead-> tunnel            = 0x00;
	phead-> group			  = 0;
	phead-> frame_len         = len + 2;    //帧长度，TLVs长度+2个字节frame_sq
	phead-> priority          = 0;  //优先级，忽略
	phead-> frame_sq          = 0;    //帧序号，原样返回
	phead-> req_resp_type     = 0;    //0请求 1应答	
	
}
/*
**初始化crc表,生成32位大小的crc表
**也可以直接定义出crc表,直接查表,
**但总共有256个,看着眼花,用生成的比较方便.
*/
static void init_crc_table(void)
{
	unsigned int c;
	unsigned int i, j;
	
	for (i = 0; i < 256; i++) {
		c = (unsigned int)i;
		for (j = 0; j < 8; j++) {
			if (c & 1)
				c = 0xedb88320L ^ (c >> 1);
			else
				c = c >> 1;
		}
		crc_table[i] = c;
	}
}
 
/*计算buffer的crc校验码*/
static unsigned int crc32(unsigned int crc,unsigned char *buffer, unsigned int size)
{
	unsigned int i;
	for (i = 0; i < size; i++) {
		crc = crc_table[(crc ^ buffer[i]) & 0xff] ^ (crc >> 8);
	}
	return crc ;
}
 
/*
**计算大文件的CRC校验码:crc32函数,是对一个buffer进行处理,
**但如果一个文件相对较大,显然不能直接读取到内存当中
**所以只能将文件分段读取出来进行crc校验,
**然后循环将上一次的crc校验码再传递给新的buffer校验函数,
**到最后，生成的crc校验码就是该文件的crc校验码.(经过测试)
*/
static int calc_img_crc(const char *buf, int len, unsigned int *img_crc)
{
	int fd;
	int nread;
	int ret;
	/*第一次传入的值需要固定,如果发送端使用该值计算crc校验码,
	**那么接收端也同样需要使用该值进行计算*/
	unsigned int crc = 0xffffffff; 

	
	crc = crc32(crc, buf, len);
	
	*img_crc = crc;
 
	close(fd);
	
	if (nread < 0) {
		printf("read.error\n");
		return -1;
	}
	
	return 0;
}

int  valuecnt(long val)
{
	if(val > 0xFFFFFF)
		return 4;
	else if(val > 0xFFFF)
		return 3;
	else if(val > 0xFF)
		return 2;
	else 
		return 1;
}
/**
  * @brief 
  */
bool my_is_big_char(char ch)
{
	if(ch >= 'A' && ch <= 'F')
		return true;
	else
		return false;
}

bool my_is_small_char(char ch)
{
	if(ch >= 'a' && ch <= 'f')
		return true;
	else
		return false;
}
bool my_isdigit(char ch)
{
	if(ch >= '0' && ch <= '9') 
		return true;
	else
		return false;
}

int dec2hex(int dec)
{
	int hex = 0;
	int val = 0;
	int loop = 0;
	do{
		val = (dec % 10) << (4*loop);
		hex |=  val;
		dec /= 10;
		loop++;
	}while(dec);
	return hex;
}
#if 0
uint8_t ascii2hex(char** q)
{
	char*p = *q;

	do{	
		if(isdigit(*p))
		{
			if(((*p == '0') && (*(p+1) == 'x')) || ((*p == '0') && (*(p+1) == 'X'))) /* '0x' '0X' */
			{
				p += 2;
				continue;	
			}	
			hex <<= 4;
			hex |=  (*p - '0');	               /* 0 1 2 3 4 5 6 7 8 9 */	
		}
		else if(my_is_big_char(*p))            /* A B C D E F */
		{
			hex <<= 4;
			hex |=  (*p - 'A') + 10;		
		}
		else if(my_is_small_char(*p))          /* a b c d e f */
		{
			hex <<= 4;
			hex |=  (*p - 'a') + 10;		
		}
		else
		{
			break;	
		}
		p++;
	}while(*p != 0); 
	

}
#endif

/**
  * @brief
  * @example  1a -> 0x1a
  *           0X9B, -> 0x9b
  *           0xab 0xcd   --> 0xcdab
  */
bool ascii2hex(char** q, int* ret_val, long* pcount)
{
	int hex = 0;
	int loop = 0;
	char* p = *q;
	long cnt = 0;
FILTER_STR:
	while((isdigit(*p) == false) && (my_is_big_char(*p) == false) && (my_is_small_char(*p) == false) && (*p != 0))
	{
		p++;
	}
	if(*p == 0)
	{
		*q = p; 
		return false;
	}

	do{	
		if(isdigit(*p))
		{

			if(((*p == '0') && (*(p+1) == 'x')) || ((*p == '0') && (*(p+1) == 'X'))) /* '0x' '0X' */
			{
				p += 2;
				goto FILTER_STR;	
			}	
			hex <<= 4;
			hex |=  (*p - '0');	               /* 0 1 2 3 4 5 6 7 8 9 */	
			cnt <<= 4;
			cnt |= 0x0F;
		}
		else if(my_is_big_char(*p))            /* A B C D E F */
		{
			hex <<= 4;
			hex |=  (*p - 'A') + 10;		
			cnt <<= 4;
			cnt |= 0x0F;
		}
		else if(my_is_small_char(*p))          /* a b c d e f */
		{
			hex <<= 4;
			hex |=  (*p - 'a') + 10;		
			cnt <<= 4;
			cnt |= 0x0F;
		}
		else
		{
			break;	
		}
		p++;
	}while(*p != 0); 
	*q = p; 
	*ret_val = hex;
	*pcount = cnt;

	return true; 
}

